/*
 * Copyright (C) 2019 ~ 2022 Uniontech Technology Co., Ltd.
 *
 * Author:     zhangbingyi <zhangbingyi@uniontech.com>
 *
 * Maintainer: zhangbingyi <zhangbingyi@uniontech.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "exefilesprotectionwidget.h"
#include "common.h"
#include "compixmap.h"
#include "widgets/ddialog.h"

//#include <DLabel>
//#include <DFontSizeManager>

#include <QLabel>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QFileDialog>
#include <QTableView>


ExeFilesProtectionWidget::ExeFilesProtectionWidget(ExeFilesProtectionModel *model, QWidget *parent)
    : QFrame(parent)
    , m_model(model)
    , m_exeFilesItem(nullptr)
    , m_tableView(nullptr)
    , m_tableModel(nullptr)
    , m_exeFilesProtectionDelegate(new ExeFilesProtectionDelegate(this))
    , m_globalCheckBox(new QCheckBox(this))
    , m_counterLable(nullptr)
    , m_addContentButton(nullptr)
    , m_removeContentButton(nullptr)
    , m_policyKitHelper(nullptr)
    , m_modelSettingChangeDialog(nullptr)
    , m_addTrustAskDialog(nullptr)
{
    // 初始化
    initUI();
    initData();
}

ExeFilesProtectionWidget::~ExeFilesProtectionWidget()
{
    m_policyKitHelper->deleteLater();
    m_policyKitHelper = nullptr;

    // 完成后删除线程
    m_polKitHelperThread.quit();
    m_polKitHelperThread.wait();
}

void ExeFilesProtectionWidget::initUI()
{
    // 主布局
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(10, 20, 10, 10);
    mainLayout->setSpacing(10);

    // 标题
    QLabel *titleLabel = new QLabel(tr("Execution Prevention"), this);
    titleLabel->setContentsMargins(10, 0, 0, 0);
    QFont font1 = titleLabel->font();
    font1.setBold(true);
    titleLabel->setFont(font1);
//    DFontSizeManager::instance()->bind(titleLabel, DFontSizeManager::T5);
    mainLayout->addWidget(titleLabel);

    // 执行保护策略
    m_exeFilesItem = new ExeFilesProtectionItem(this);
    m_exeFilesItem->setText(tr("Execution policy"));
    m_exeFilesItem->setComboBoxData(QStringList() << tr("Off") << tr("Ask") << tr("Block"),
                                    QStringList() << tr("Any executable file can run directly.")
                                                  << tr("A notification will pop up when untrusted executable files are running.")
                                                  << tr("Untrusted executable files will be blocked from running."));
    mainLayout->addWidget(m_exeFilesItem);

    // 策略列表
    m_tableView = new QTableView(this);
    m_tableView->verticalHeader()->setDefaultSectionSize(50);
    m_tableView->verticalHeader()->setVisible(false);
    m_tableView->horizontalHeader()->setStretchLastSection(true);
    m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    //单选
    m_tableView->setSelectionMode(QAbstractItemView::NoSelection);
    //不可以编辑
    m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    //形状
    m_tableView->setFrameShape(QTableView::NoFrame);
    //栅格
    m_tableView->setShowGrid(false);
    //排序
    m_tableView->setSortingEnabled(false);

    // 连接表格与模型
    m_tableView->setItemDelegate(m_exeFilesProtectionDelegate);
    m_tableModel = new QStandardItemModel(this);
    m_tableView->setModel(m_tableModel);
    mainLayout->addWidget(m_tableView);

    // 初始化表格列
    m_tableModel->setHorizontalHeaderItem(TABLE_COL_FILE_NAME, new QStandardItem(tr("File Name")));
    m_tableModel->setHorizontalHeaderItem(TABLE_COL_FILE_PATH, new QStandardItem(tr("File Path")));
    m_tableModel->setHorizontalHeaderItem(TABLE_COL_STATUS_NAME, new QStandardItem(tr("Status")));
    m_tableModel->setHorizontalHeaderItem(TABLE_COL_ACTION_NAME, new QStandardItem(tr("Action")));

    // 初始化列宽
    m_tableView->setColumnWidth(TABLE_COL_FILE_NAME, 182);
    m_tableView->setColumnWidth(TABLE_COL_FILE_PATH, 244);
    m_tableView->setColumnWidth(TABLE_COL_STATUS_NAME, 100);
    m_tableView->setColumnWidth(TABLE_COL_ACTION_NAME, 184);

    // 表格列显示/掩藏
    m_tableView->setColumnHidden(TABLE_COL_FILE_NAME, false);
    m_tableView->setColumnHidden(TABLE_COL_FILE_PATH, false);
    m_tableView->setColumnHidden(TABLE_COL_STATUS_NAME, false);
    m_tableView->setColumnHidden(TABLE_COL_ACTION_NAME, false);
    m_tableView->horizontalHeader()->setHighlightSections(false);

    // 策略列表操作
    m_globalCheckBox->setText(tr("Select All"));
    m_globalCheckBox->setEnabled(false);
    // 统计值
    m_counterLable = new QLabel(this);

    // 按钮
    m_addContentButton = new QPushButton(this);
    m_addContentButton->setText(tr("Add", "button"));
    m_addContentButton->setFixedSize(160, 36);

    m_removeContentButton = new QPushButton(this);
    m_removeContentButton->setText(tr("Unprotect", "button"));
    m_removeContentButton->setFixedSize(160, 36);
    m_removeContentButton->setEnabled(false);

    QGridLayout *bottomLayout = new QGridLayout;
    bottomLayout->setContentsMargins(10, 18, 10, 0);
    bottomLayout->setSpacing(9);
    bottomLayout->setColumnMinimumWidth(0, 20);
    bottomLayout->addWidget(m_globalCheckBox, 0, 0, Qt::AlignLeft);
    bottomLayout->setColumnMinimumWidth(1, 356);
    bottomLayout->addWidget(m_counterLable, 0, 1, Qt::AlignLeft);
    bottomLayout->setColumnMinimumWidth(2, 160);
    bottomLayout->addWidget(m_removeContentButton, 0, 2, Qt::AlignRight);
    bottomLayout->setColumnMinimumWidth(3, 160);
    bottomLayout->addWidget(m_addContentButton, 0, 3, Qt::AlignRight);

    mainLayout->addLayout(bottomLayout);
}

// 初始化数据
void ExeFilesProtectionWidget::initData()
{
    // 初始化执行保护模式
    m_exeFilesItem->setComboBoxDefaultData(m_model->getExeFilesMode());

    // 初始化执行保护列表
    m_model->queryExeFilesList();

    // 初始化信号槽
    connect(m_model, SIGNAL(sendExeFilesList(QList<ExeFilesInfo>)), this, SLOT(loadTableData(QList<ExeFilesInfo>)));
    connect(m_exeFilesProtectionDelegate, SIGNAL(sendCheckClickSingal()), this, SLOT(checkBoxAllStatus()));
    connect(m_exeFilesProtectionDelegate, SIGNAL(sendTrustFile(const QModelIndex &)), this, SLOT(acceptTrustFile(const QModelIndex &)));
    connect(m_globalCheckBox, SIGNAL(clicked(bool)), this, SLOT(selectAll(bool)));
    connect(m_addContentButton, SIGNAL(pressed()), this, SLOT(addExeFile()));
    connect(m_removeContentButton, SIGNAL(pressed()), this, SLOT(deleteExeFiles()));
    connect(m_exeFilesItem, SIGNAL(sendComboBoxChangeIndex(int)), this, SLOT(acceptComboBoxChangeIndex(int)));

    // thread
    m_policyKitHelper = new ExeFilesPolicyKitHelper;
    m_policyKitHelper->moveToThread(&m_polKitHelperThread);

    // 可信文件移除
    connect(this, SIGNAL(requestConfirmRemoveExeFiles(QStringList)), m_policyKitHelper, SLOT(confirmRemoveExeFiles(QStringList)));
    connect(m_policyKitHelper, SIGNAL(notifyRemoveExeFiles(bool, QStringList)), this,SLOT(onPolicyNotifyRemoveExeFiles(bool, QStringList)));

    // 信任篡改文件
    connect(this, SIGNAL(requestConfirmTrustExeFiles(const QString &)), m_policyKitHelper, SLOT(confirmTrustExeFiles(const QString &)));
    connect(m_policyKitHelper, SIGNAL(notifyTrustExeFiles(bool, const QString&)), this, SLOT(onPolicyNotifyTrustExeFiles(bool, const QString&)));

    // 执行保护策略设置
    connect(this, SIGNAL(requestConfirmModelSetting(int)), m_policyKitHelper, SLOT(confirmModelSetting(int)));
    connect(m_policyKitHelper, SIGNAL(notifyModelSetting()), this, SLOT(onPolicyNotifyModelSetting(bool, int )));

    m_polKitHelperThread.start();
}

// 加载表格数据
void ExeFilesProtectionWidget::loadTableData(QList<ExeFilesInfo> infoList)
{
    //清空上次扫描的数据
    m_globalCheckBox->setCheckState(Qt::CheckState::Unchecked);
    int count = m_tableModel->rowCount();
    for (int i = count; i >= 0; i--) {
        m_tableModel->removeRow(i);
    }

    // 判断数据是否为空
    if (infoList.count() <= 0) {
        m_globalCheckBox->setEnabled(false);
        m_removeContentButton->setEnabled(false);
        showTotalInfo(0, 0);
        return;
    }

    int nCountValid = 0;
    foreach (auto info, infoList) {
        QString sFilePath = info.filepath;
        QFile f(sFilePath);
        if (f.exists()) {
            QStringList sFilePathList = sFilePath.split('/', QString::SkipEmptyParts);
            QString sFileName = sFilePathList.at(sFilePathList.count() - 1);
            int nStatus = info.status;

            // 数据加载
            int row = m_tableModel->rowCount();
            QStandardItem *c0 = new QStandardItem();
            QStandardItem *c1 = new QStandardItem();
            QStandardItem *c2 = new QStandardItem();
            QStandardItem *c3 = new QStandardItem();
            m_tableModel->setItem(row, TABLE_COL_FILE_NAME, c0);
            m_tableModel->setItem(row, TABLE_COL_FILE_PATH, c1);
            m_tableModel->setItem(row, TABLE_COL_STATUS_NAME, c2);
            m_tableModel->setItem(row, TABLE_COL_ACTION_NAME, c3);
            c0->setData(false, ROLE_CHECKED_STATUS);
            c0->setData(sFileName, ROLE_FILE_NAME);
            c1->setData(sFilePath, ROLE_FILE_PATH);
            c2->setData(nStatus, ROLE_FILE_STATUS);
            c3->setData(nStatus, ROLE_FILE_ACTION);

            nCountValid += 1;
        }
    }

    // 列表数据为空，置灰全选框
    if (nCountValid > 0) {
        m_globalCheckBox->setEnabled(true);
    } else {
        m_globalCheckBox->setEnabled(false);
    }

    // 显示统计信息
    showTotalInfo(nCountValid, 0);
    m_removeContentButton->setEnabled(false);
}

// 界面上全选 反选逻辑
void ExeFilesProtectionWidget::selectAll(bool state)
{
    int nCount = m_tableModel->rowCount();
    for (int i = 0; i < m_tableModel->rowCount(); ++i) {
        QModelIndex index = m_tableModel->index(i, TABLE_COL_FILE_NAME);
        m_tableModel->setData(index, state, ROLE_CHECKED_STATUS);
    }

    // 显示统计信息
    int nSelectCount = 0;
    showTotalInfo(nCount, state ? nCount : nSelectCount);
    m_removeContentButton->setEnabled(state);
}

// 判断全选按钮状态
void ExeFilesProtectionWidget::checkBoxAllStatus()
{
    // 总行数
    int count = m_tableModel->rowCount();
    // 打沟的数量
    int tcount = 0;
    // 不打沟的数量
    int fcount = 0;
    for (int i = 0; i < m_tableModel->rowCount(); ++i) {
        QModelIndex index = m_tableModel->index(i, TABLE_COL_FILE_NAME);
        bool status = m_tableModel->data(index, ROLE_CHECKED_STATUS).toBool();
        if (status) {
            tcount += 1;
        } else {
            fcount += 1;
        }
    }

    // 根据数量判断  全选状态
    if (tcount == count) {
        m_globalCheckBox->setCheckState(Qt::CheckState::Checked);
    } else {
        m_globalCheckBox->setCheckState(Qt::CheckState::Unchecked);
    }

    // 显示统计信息
    showTotalInfo(count, tcount);
    m_removeContentButton->setEnabled(tcount > 0);
}

// 显示统计信息
void ExeFilesProtectionWidget::showTotalInfo(int nCount, int nSelectCount)
{
    m_counterLable->setText(tr("%1 rules (%2 selected)").arg(nCount).arg(nSelectCount));
}

void ExeFilesProtectionWidget::onPolicyNotifyRemoveExeFiles(bool result, QStringList sFiles)
{
    if (result) {
        m_model->deleteTrustFiles(sFiles);
    }
    this->parentWidget()->parentWidget()->setEnabled(true);
}

void ExeFilesProtectionWidget::onPolicyNotifyTrustExeFiles(bool result, const QString &file)
{
    if (result) {
        m_model->trustCurrentStatus(file);
    }
    this->parentWidget()->parentWidget()->setEnabled(true);
}

void ExeFilesProtectionWidget::onPolicyNotifyModelSetting(bool result, int model)
{
    if (result) {
        m_model->setExeFilesMode(model);
    } else {
        int nDefaultIndex = m_model->getExeFilesMode();
        m_exeFilesItem->setComboBoxDefaultData(nDefaultIndex);
    }
    this->parentWidget()->parentWidget()->setEnabled(true);
}

static int gnIndex;
void ExeFilesProtectionWidget::sendExeFilesModelTurnOff()
{
    m_isSettingModelFlag = false;
    Q_EMIT requestConfirmModelSetting(gnIndex);
}

static int gnDefaultIndex;
void ExeFilesProtectionWidget::onDialogClose()
{
    m_modelSettingChangeDialog = nullptr;
    if (m_isSettingModelFlag) {
        m_exeFilesItem->setComboBoxDefaultData(gnDefaultIndex);
    }
}

static QModelIndex sendAddTrustFileIndex;
void ExeFilesProtectionWidget::sendAddTrustFile()
{
    m_isAddTrustFlag = false;

    this->parentWidget()->parentWidget()->setEnabled(false);
    QModelIndex indexPath = m_tableModel->index(sendAddTrustFileIndex.row(), TABLE_COL_FILE_PATH);
    Q_EMIT requestConfirmTrustExeFiles(m_tableModel->data(indexPath, ROLE_FILE_PATH).toString());
}

void ExeFilesProtectionWidget::onAddTrustDialogClose()
{
    m_addTrustAskDialog = nullptr;
}

void ExeFilesProtectionWidget::addressChangeDialogHelp(int index, QString)
{
    DDialog* dialog = qobject_cast<DDialog*>(sender());
    switch (index) {
    case 0:
        dialog->close();
        break;
    default:
        break;
    }
}

// 接收下拉框改变状态
void ExeFilesProtectionWidget::acceptComboBoxChangeIndex(int nIndex)
{
    m_isSettingModelFlag = true;
    int nDefaultIndex = m_model->getExeFilesMode();
    if (nIndex < nDefaultIndex) {
        if (nIndex) {
            Q_EMIT requestConfirmModelSetting(nIndex);
        } else {
            m_modelSettingChangeDialog = new ModelSettingChangeDialog();
            gnIndex =nIndex;
            connect(m_modelSettingChangeDialog, SIGNAL(sendExeFilesModelTurnOff()), this, SLOT(sendExeFilesModelTurnOff));
            gnDefaultIndex = nDefaultIndex;
            connect(m_modelSettingChangeDialog, SIGNAL(onDialogClose()), this, SLOT(onDialogClose()));

            m_modelSettingChangeDialog->setAttribute(Qt::WA_DeleteOnClose);
            m_modelSettingChangeDialog->setOnButtonClickedClose(true);
            m_modelSettingChangeDialog->show();
        }
    } else {
        m_model->setExeFilesMode(nIndex);
    }
}

// 接收信任当前状态
void ExeFilesProtectionWidget::acceptTrustFile(const QModelIndex &index)
{
    m_isAddTrustFlag = true;
    m_addTrustAskDialog = new AddTrustAskDialog();
    sendAddTrustFileIndex = index;
    connect(m_addTrustAskDialog, SIGNAL(sendAddTrustFile()), this, SLOT(sendAddTrustFile()));
    connect(m_addTrustAskDialog, SIGNAL(onDialogClose()), this, SLOT(onAddTrustDialogClose()));
    m_addTrustAskDialog->setAttribute(Qt::WA_DeleteOnClose);
    m_addTrustAskDialog->setOnButtonClickedClose(true);
    m_addTrustAskDialog->show();
}

// 添加执行文件
void ExeFilesProtectionWidget::addExeFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open file"), QDir::homePath(), tr("All Files(*)"));
    // 按钮归位
    // DSuggestButton响应pressed信号时，为什么按下后不自动归位
    m_addContentButton->setDown(false);

    // 选择时退出了
    if (fileName.isEmpty()) {
        return;
    }

    // 结果不正确时弹框
    if (!verifyContentFileType(fileName)) {
        DDialog *dialog = new DDialog;
        dialog->setAccessibleName("addressChangeDialog");
        dialog->setMinimumSize(QSize(TIP_DIALOG_WIDTH, TIP_DIALOG_HEIGHT));
        dialog->setMaximumSize(QSize(TIP_DIALOG_WIDTH, TIP_DIALOG_HEIGHT));
        dialog->setIcon(QIcon::fromTheme(DIALOG_TIP_YELLOW));
        dialog->setModal(true);
        dialog->setMessage(tr("Select a right file please"));
        dialog->setWordWrapMessage(true);
        dialog->setAttribute(Qt::WA_DeleteOnClose);
        // 确定推荐
        QPushButton *okBtn = new QPushButton(dialog);
        okBtn->setText(tr("OK", "button"));
        okBtn->setObjectName("okButton");
        okBtn->setAccessibleName("fdialog_okButton");
        okBtn->setAttribute(Qt::WA_NoMousePropagation);
        dialog->insertButton(0, okBtn, false);
        connect(dialog, SIGNAL(buttonClicked(int, QString)), this, SLOT(addressChangeDialogHelp(int, QString)));
        dialog->show();
    } else {
        m_model->addExeFile(fileName);
    }
}

// 前端检查加入的文件类型
bool ExeFilesProtectionWidget::verifyContentFileType(const QString &path)
{
    // 判断一下是否符号链接，是的话则不处理
    QFileInfo file(path);
    if (file.exists()) {
        return true;
    }

    return false;
}

// 移除保护
void ExeFilesProtectionWidget::deleteExeFiles()
{
    QStringList sDeleteFiles;
    for (int i = 0; i < m_tableModel->rowCount(); ++i) {
        QModelIndex index = m_tableModel->index(i, TABLE_COL_FILE_NAME);
        bool status = m_tableModel->data(index, ROLE_CHECKED_STATUS).toBool();
        if (status) {
            QModelIndex indexPath = m_tableModel->index(i, TABLE_COL_FILE_PATH);
            QString sFilePath = m_tableModel->data(indexPath, ROLE_FILE_PATH).toString();
            if (!sFilePath.isEmpty()) {
                sDeleteFiles.append(sFilePath);
            }
        }
    }

    if (sDeleteFiles.count() >= 0) {
        this->parentWidget()->parentWidget()->setEnabled(false);
        Q_EMIT requestConfirmRemoveExeFiles(sDeleteFiles);
    }
}
